package info.hexin.aop3.test3;

import info.hexin.aop3.AopInjectClass;
import info.hexin.aop3.AopInjectClass1;
import info.hexin.aop3.AsmClassLoader;
import info.hexin.aop3.ClassAdapter;
import info.hexin.aop3.Foo;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

public class ClassWriterAopExample3 implements Opcodes {

    public static void main(String[] args) throws Exception {

        AsmClassLoader asmClassLoader = new AsmClassLoader();
        ClassWriter cw = initClass(asmClassLoader);

        FieldVisitor fv = cw.visitField(Opcodes.ACC_PRIVATE, "ttttt", Type.getDescriptor(AopInjectClass1.class), null,
                null);
        fv.visitEnd();

        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "execute", "()V", null, null);
        // 开始增加代码
        mv.visitCode();

        // 赋值
        mv.visitVarInsn(ALOAD, 0);
        mv.visitTypeInsn(Opcodes.NEW, "info/hexin/aop3/AopInjectClass1");
        mv.visitInsn(DUP);
        mv.visitMethodInsn(INVOKESPECIAL, "info/hexin/aop3/AopInjectClass1", "<init>", "()V");
        mv.visitFieldInsn(Opcodes.PUTFIELD, "info/hexin/aop3/Foo$1", "ttttt", Type.getDescriptor(AopInjectClass1.class));

        //调用
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(Opcodes.GETFIELD, "info/hexin/aop3/Foo$1", "ttttt", Type.getDescriptor(AopInjectClass1.class));
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "info/hexin/aop3/AopInjectClass1", "test2", "()V");

        mv.visitInsn(Opcodes.RETURN);
        mv.visitMaxs(0, 0); // 这个地方，最大的操作数栈和最大的本地变量的空间，是自动计算的，是因为构造ClassWriter的时候使用了ClassWriter.COMPUTE_MAXS
        mv.visitEnd();

        Thread.currentThread().setContextClassLoader(asmClassLoader);
        Class<?> c = asmClassLoader.defineClassFromClassFile("info.hexin.aop3.Foo$1", cw.toByteArray());
        printfield(c);
        printmethod(c);
        invoke(c);
    }

    private static ClassWriter initClass(AsmClassLoader asmClassLoader) throws ClassNotFoundException, IOException {
        Thread.currentThread().setContextClassLoader(asmClassLoader);
        Class.forName(AopInjectClass.class.getName());
        ClassReader cr = new ClassReader(Foo.class.getName());
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        cr.accept(new ClassAdapter(Opcodes.ASM4, cw), 0);
        return cw;
    }

    private static void printfield(Class<?> c) {
        Field[] fs = c.getDeclaredFields();
        for (Field field : fs) {
            System.out.println(field);
        }
    }

    private static void invoke(Class<?> c) throws InstantiationException, IllegalAccessException,
            NoSuchMethodException, InvocationTargetException {
        Object instances = c.newInstance();
        Method m = c.getMethod("execute", null);
        m.invoke(instances, null);

        //
        // System.out.println("execute$1");
        //
        // Method m1 = c.getMethod("execute$1", null);
        // m1.invoke(instances, null);
    }

    private static void printmethod(Class<?> c) {
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
    }
}
